package com.breeze.media.camera;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import com.breeze.media.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

/**
 * 支持双景拍摄，处理流程和单景相同
 */
public class CameraActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private static final SparseIntArray ORIENTATION = new SparseIntArray();

    static {
        ORIENTATION.append(Surface.ROTATION_0, 90);
        ORIENTATION.append(Surface.ROTATION_90, 0);
        ORIENTATION.append(Surface.ROTATION_180, 270);
        ORIENTATION.append(Surface.ROTATION_270, 180);
    }

    private String mCameraId;
    private String mBackCameraId;
    private Size mPreviewSize;
    private Size mBackPreviewSize;
    private Size mCaptureSize;
    private Size mBackCaptureSize;
    private HandlerThread mCameraThread;
    private HandlerThread mBackCameraThread;
    private Handler mCameraHandler;
    private Handler mBackCameraHandler;
    private CameraDevice mCameraDevice;
    private CameraDevice mBackCameraDevice;
    private TextureView mTextureFrontView;
    private TextureView mTextureBackView;
    private ImageReader mImageReader;
    private ImageReader mBackImageReader;
    private CaptureRequest.Builder mCaptureRequestBuilder;
    private CaptureRequest.Builder mBackCaptureRequestBuilder;
    private CaptureRequest mCaptureRequest;
    private CaptureRequest mBackCaptureRequest;
    private CameraCaptureSession mCameraCaptureSession;
    private CameraCaptureSession mBackCameraCaptureSession;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_camera);
        mTextureFrontView = findViewById(R.id.texture_front_view);
        mTextureBackView = findViewById(R.id.texture_back_view);
    }

    @Override
    protected void onResume() {
        super.onResume();
        startCameraThread();
        if (!mTextureFrontView.isAvailable()) {
            Log.i(TAG, "onResume: 1");
            mTextureFrontView.setSurfaceTextureListener(mTextureFrontListener);
        } else {
            startPreview();
        }
        if (!mTextureBackView.isAvailable()) {
            Log.i(TAG, "onResume: 2");
            mTextureBackView.setSurfaceTextureListener(mTextureBackListener);
        } else {
            startBackPreview();
        }
    }

    private void startCameraThread() {
        mCameraThread = new HandlerThread("CameraThread");
        mCameraThread.start();
        mCameraHandler = new Handler(mCameraThread.getLooper());
        mBackCameraThread = new HandlerThread("CameraThreadBack");
        mBackCameraThread.start();
        mBackCameraHandler = new Handler(mCameraThread.getLooper());
    }

    private TextureView.SurfaceTextureListener mTextureFrontListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            // 当SurefaceTexture可用的时候，设置相机参数并打开相机
            Log.i(TAG, "onSurfaceTextureAvailable: ");
            setupCamera(width, height);
            openCamera();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {

        }
    };

    private TextureView.SurfaceTextureListener mTextureBackListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            Log.i(TAG, "onSurfaceTextureAvailable: back");
            setupBackCamera(width, height);
            openBackCamera();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {

        }
    };


    // 1. 选择摄像头，预览及拍摄尺寸，设置ImageReader
    private void setupCamera(int width, int height) {
        // 获取摄像头的管理者CameraManager
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            // 遍历所有摄像头
            for (String cameraId : manager.getCameraIdList()) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                // 此处默认打开前置摄像头
                Log.i(TAG, "setupCamera: " + cameraId + ", facing=" + facing);
                if (facing != null && facing != CameraCharacteristics.LENS_FACING_FRONT)
                    continue;
                // 获取StreamConfigurationMap，它是管理摄像头支持的所有输出格式和尺寸
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                assert map != null;
                // 根据TextureView的尺寸设置预览尺寸
                mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                // 获取相机支持的最大拍照尺寸
                mCaptureSize = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() {
                    @Override
                    public int compare(Size lhs, Size rhs) {
                        return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth());
                    }
                });
                // 此ImageReader用于拍照所需
                setupImageReader();
                mCameraId = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void setupBackCamera(int width, int height) {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            // 遍历所有摄像头
            for (String cameraId : manager.getCameraIdList()) {
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                // 此处默认打开后置摄像头
                Log.i(TAG, "setupBackCamera: " + cameraId + ", facing=" + facing);
                if (facing != null && facing != CameraCharacteristics.LENS_FACING_BACK)
                    continue;
                // 获取StreamConfigurationMap，它是管理摄像头支持的所有输出格式和尺寸
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                assert map != null;
                // 根据TextureView的尺寸设置预览尺寸
                mBackPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                // 获取相机支持的最大拍照尺寸
                mBackCaptureSize = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() {
                    @Override
                    public int compare(Size lhs, Size rhs) {
                        return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth());
                    }
                });
                // 此ImageReader用于拍照所需
                setupBackImageReader();
                mBackCameraId = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    //选择sizeMap中大于并且最接近width和height的size
    private Size getOptimalSize(Size[] sizeMap, int width, int height) {
        List<Size> sizeList = new ArrayList<>();
        for (Size option : sizeMap) {
            if (width > height) {
                if (option.getWidth() > width && option.getHeight() > height) {
                    sizeList.add(option);
                }
            } else {
                if (option.getWidth() > height && option.getHeight() > width) {
                    sizeList.add(option);
                }
            }
        }
        if (sizeList.size() > 0) {
            return Collections.min(sizeList, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        return sizeMap[0];
    }


    private void openCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            // 打开Camera,传入StateCallback，
            manager.openCamera(mCameraId, mStateCallback, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void openBackCamera() {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        // 打开Camera,传入StateCallback，
        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(mBackCameraId, mBackStateCallback, mBackCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            Log.i(TAG, "onOpened: ");
            mCameraDevice = camera;
            startPreview();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            Log.i(TAG, "onDisconnected: ");
            camera.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            Log.i(TAG, "onError: ");
            camera.close();
            mCameraDevice = null;
        }
    };
    private CameraDevice.StateCallback mBackStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            Log.i(TAG, "back onOpened: ");
            mBackCameraDevice = camera;
            startBackPreview();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            Log.i(TAG, "back onDisconnected: ");
            camera.close();
            mBackCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            Log.i(TAG, "back onError: " + error);
            camera.close();
            mBackCameraDevice = null;
        }
    };

    // 开始预览
    private void startPreview() {
        SurfaceTexture surfaceTexture = mTextureFrontView.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        Surface previewSurface = new Surface(surfaceTexture);
        try {
            mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mCaptureRequestBuilder.addTarget(previewSurface);
            // 1. 创建拍照会话会返回CameraCaptureSession，传入Surface用于显示预览画面
            mCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    Log.i(TAG, "onConfigured: ");
                    try {
                        mCaptureRequest = mCaptureRequestBuilder.build();
                        mCameraCaptureSession = session;
                        // 2. 通过CameraCaptureSession可以发起请求
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, new CameraCaptureSession.CaptureCallback() {
                            @Override
                            public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session, int sequenceId, long frameNumber) {
                                super.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
                                Log.i(TAG, "onCaptureSequenceCompleted: sequenceId=" + sequenceId + ", " + frameNumber);
                            }
                        }, mCameraHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {

                }
            }, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void startBackPreview() {
        SurfaceTexture surfaceTexture = mTextureBackView.getSurfaceTexture();
        surfaceTexture.setDefaultBufferSize(mBackPreviewSize.getWidth(), mBackPreviewSize.getHeight());
        Surface previewSurface = new Surface(surfaceTexture);
        try {
            mBackCaptureRequestBuilder = mBackCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mBackCaptureRequestBuilder.addTarget(previewSurface);
            // 1. 创建拍照会话会返回CameraCaptureSession，传入Surface用于显示预览画面
            mBackCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mBackImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(CameraCaptureSession session) {
                    Log.i(TAG, "onConfigured back: ");
                    try {
                        mBackCaptureRequest = mBackCaptureRequestBuilder.build();
                        mBackCameraCaptureSession = session;
                        // 2. 通过CameraCaptureSession可以发起请求
                        mBackCameraCaptureSession.setRepeatingRequest(mBackCaptureRequest, new CameraCaptureSession.CaptureCallback() {
                            @Override
                            public void onCaptureSequenceCompleted(@NonNull CameraCaptureSession session, int sequenceId, long frameNumber) {
                                super.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
                                Log.i(TAG, "back onCaptureSequenceCompleted: sequenceId=" + sequenceId + ", " + frameNumber);
                            }
                        }, mBackCameraHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(CameraCaptureSession session) {

                }
            }, mBackCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    public void takePicture(View view) {
        lockFocus();
    }

    private void lockFocus() {
        try {
            mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
            // 1. 利用CameraCaptureSession 传入对角参数
            mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), mCaptureCallback, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        try {
            mBackCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
            mBackCameraCaptureSession.capture(mBackCaptureRequestBuilder.build(), mBackCaptureCallback, mBackCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
            Log.i(TAG, "onCaptureProgressed: ");
        }

        @Override
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
            Log.i(TAG, "onCaptureCompleted: ");
            try {
                final CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                mCaptureBuilder.addTarget(mImageReader.getSurface());
                mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));
                CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
                    @Override
                    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                        Toast.makeText(getApplicationContext(), "Image Saved!", Toast.LENGTH_SHORT).show();
                        unLockFocus();
                    }
                };
                mCameraCaptureSession.stopRepeating();
                // 利用CameraCaptureSession传入拍照参数
                mCameraCaptureSession.capture(mCaptureBuilder.build(), captureCallback, null);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    };
    private CameraCaptureSession.CaptureCallback mBackCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) {
            Log.i(TAG, "onCaptureProgressed: ");
        }

        @Override
        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
            Log.i(TAG, "onCaptureCompleted: ");
            try {
                final CaptureRequest.Builder mCaptureBuilder = mBackCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                int rotation = getWindowManager().getDefaultDisplay().getRotation();
                mCaptureBuilder.addTarget(mBackImageReader.getSurface());
                mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));
                CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
                    @Override
                    public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                        Toast.makeText(getApplicationContext(), "Back Image Saved!", Toast.LENGTH_SHORT).show();
                        unLockBackFocus();
                    }
                };
                mBackCameraCaptureSession.stopRepeating();
                // 利用CameraCaptureSession传入拍照参数
                mBackCameraCaptureSession.capture(mCaptureBuilder.build(), captureCallback, null);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    };

    private void unLockBackFocus() {
        try {
            mBackCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            //mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), null, mCameraHandler);
            mBackCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mBackCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }


    private void unLockFocus() {
        try {
            mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            //mCameraCaptureSession.capture(mCaptureRequestBuilder.build(), null, mCameraHandler);
            mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mCameraCaptureSession != null) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }

        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }

        if (mImageReader != null) {
            mImageReader.close();
            mImageReader = null;
        }
    }

    private void setupImageReader() {
        // 2代表ImageReader中最多可以获取两帧图像流
        mImageReader = ImageReader.newInstance(mCaptureSize.getWidth(), mCaptureSize.getHeight(),
                ImageFormat.JPEG, 2);
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Log.i(TAG, "onImageAvailable: ");
                Image image = reader.acquireNextImage();
                mCameraHandler.post(new ImageSaver(image));
            }
        }, mCameraHandler);
    }

    private void setupBackImageReader() {
        mBackImageReader = ImageReader.newInstance(mBackCaptureSize.getWidth(), mBackCaptureSize.getHeight(),
                ImageFormat.JPEG, 2);
        mBackImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                Image image = reader.acquireNextImage();
                mBackCameraHandler.post(new ImageSaver(image));
            }
        }, mBackCameraHandler);
    }

    // 用于处理图像数据的保存
    public static class ImageSaver implements Runnable {

        private Image mImage;

        public ImageSaver(Image image) {
            mImage = image;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);
            String path = Environment.getExternalStorageDirectory() + "/DCIM/CameraV2/";
            File mImageFile = new File(path);
            if (!mImageFile.exists()) {
                mImageFile.mkdir();
            }
            String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
            String fileName = path + "IMG_" + timeStamp + ".jpg";
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(fileName);
                fos.write(data, 0, data.length);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mImage.close();
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}